﻿using Apewer.Network;
using Apewer.Source;
using Newtonsoft.Json.Linq;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Reflection;
using System.Text;

namespace Apewer.Web
{

    /// <summary></summary>
    public static class ApiUtility
    {

        #region Text

        /// <summary>修剪 IP 地址，去除无效的部分。</summary>
        public static string TrimIP(string text)
        {
            var trimmed = TextUtility.Trim(text);
            if (string.IsNullOrEmpty(trimmed)) return "";

            var ip = TextUtility.Trim(trimmed.Split(':')[0]);
            return NetworkUtility.IsIP(ip) ? ip : "";
        }

        /// <summary>按 &amp; 拆分多个参数。</summary>
        public static StringPairs Parameters(string query, bool decode = true)
        {
            var input = query;
            var list = new StringPairs();
            if (!string.IsNullOrEmpty(input))
            {
                if (input[0] == '?') input = input.Substring(1);
                var args = input.Split('&');
                foreach (var arg in args)
                {
                    var equals = arg.IndexOf("=");
                    var left = equals < 0 ? arg : arg.Substring(0, equals);
                    var right = equals < 0 ? "" : arg.Substring(equals + 1);
                    if (decode)
                    {
                        left = TextUtility.DecodeUrl(left);
                        right = TextUtility.DecodeUrl(right);
                    }
                    list.Add(left, right);
                }
            }
            return list;
        }

        /// <summary>获取参数并解码，可要求修剪参数值。</summary>
        public static string Parameter(string encoded, bool trim, params string[] names)
        {
            if (string.IsNullOrEmpty(encoded)) return null;
            if (names == null || names.Length < 1) return null;

            // Names 转为小写，加强适配。
            var lowerNames = new List<string>(names.Length);
            foreach (var name in names)
            {
                var lower = TextUtility.Lower(name);
                if (string.IsNullOrEmpty(lower)) continue;
                lowerNames.Add(lower);
            }
            if (lowerNames.Count < 1) return null;

            // 分参数对比。
            var parameters = Parameters(encoded);
            var matched = false;
            foreach (var parameter in parameters)
            {
                var left = parameter.Key;
                var right = parameter.Value;
                if (trim) right = TextUtility.Lower(right);
                var lowerLeft = TextUtility.Lower(left);
                if (lowerNames.Contains(right))
                {
                    matched = true;
                    if (!string.IsNullOrEmpty(right)) return right;
                }
            }

            return matched ? "" : null;
        }

        /// <summary>获取参数，可要求修剪参数值。</summary>
        public static string Parameter(StringPairs parameters, bool trim, params string[] names)
        {
            if (parameters == null || parameters.Count < 1) return null;
            if (names == null || names.Length < 1) return null;

            var lowerNames = new List<string>(names.Length);
            foreach (var name in names)
            {
                var lower = TextUtility.Lower(name);
                if (string.IsNullOrEmpty(lower)) continue;
                lowerNames.Add(lower);
            }

            foreach (var parameter in parameters)
            {
                var lowerKey = TextUtility.Lower(parameter.Key);
                if (lowerNames.Contains(lowerKey))
                {
                    var value = parameter.Value;
                    if (trim) value = TextUtility.Trim(value);
                    if (!string.IsNullOrEmpty(value)) return value;
                }
            }
            return null;
        }

        /// <summary>获取 User Agent。</summary>
        public static string UserAgent(HttpHeaders headers) => headers == null ? null : headers.GetValue("user-agent");

        // 从 Uri 对象中解析路径片段。
        private static string[] Segmentals(Uri url)
        {
            if (url == null) return null;
            if (string.IsNullOrEmpty(url.AbsolutePath)) return null;
            var segmentals = url.AbsolutePath.Split('/');
            return segmentals;
        }

        // 获取已经解析的路径片段。
        private static string Segmental(string[] segmentals, int index = 3, bool decode = false)
        {
            if (segmentals == null || segmentals.Length < 1) return null;
            if (index < 1 || index >= segmentals.Length) return null;
            var segmental = segmentals[index];
            if (decode) segmental = TextUtility.DecodeUrl(segmental);
            return segmental;
        }

        #endregion

        #region Headers

        /// <summary></summary>
        public static HttpMethod Method(string method)
        {
            if (!string.IsNullOrEmpty(method))
            {
                var upper = TextUtility.Upper(method);
                if (upper.Contains("OPTIONS")) return HttpMethod.OPTIONS;
                else if (upper.Contains("POST")) return HttpMethod.POST;
                else if (upper.Contains("GET")) return HttpMethod.GET;
                else if (upper.Contains("CONNECT")) return HttpMethod.CONNECT;
                else if (upper.Contains("DELETE")) return HttpMethod.DELETE;
                else if (upper.Contains("HEAD")) return HttpMethod.HEAD;
                else if (upper.Contains("PATCH")) return HttpMethod.PATCH;
                else if (upper.Contains("PUT")) return HttpMethod.PUT;
                else if (upper.Contains("TRACE")) return HttpMethod.TRACE;
            }
            return HttpMethod.NULL;
        }

        /// <summary>获取 X-Forwarded-For，不存在时返回 NULL 值。</summary>
        public static string[] GetForwardedIP(HttpHeaders headers)
        {
            if (headers != null)
            {
                var value = headers.GetValue("x-forwarded-for");
                if (!string.IsNullOrEmpty(value))
                {
                    var fips = new List<string>();
                    var split = value.Split(',', ' ');
                    foreach (var para in split)
                    {
                        var fip = TrimIP(para);
                        if (!string.IsNullOrEmpty(fip)) fips.Add(fip);
                    }
                    return fips.ToArray();
                }
            }
            return new string[0];
        }

        #endregion

        #region Cookies

        // https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Set-Cookie

        // <cookie-name> 可以是除了控制字符 (CTLs)、空格 (spaces) 或制表符 (tab)之外的任何 US-ASCII 字符。
        // 同时不能包含以下分隔字符： ( ) < > @ , ; : \ " /  [ ] ? = { }.

        // <cookie-value> 是可选的，如果存在的话，那么需要包含在双引号里面。
        // 支持除了控制字符（CTLs）、空格（whitespace）、双引号（double quotes）、逗号（comma）、分号（semicolon）以及反斜线（backslash）之外的任意 US-ASCII 字符。

        // 关于编码：许多应用会对 cookie 值按照URL编码（URL encoding）规则进行编码，但是按照 RFC 规范，这不是必须的。
        // 不过满足规范中对于 <cookie-value> 所允许使用的字符的要求是有用的。

        const string CookieChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&*+-<>^_`|~";

        /// <summary>修剪 Cookie 的 Name 文本。</summary>
        public static string CookieName(string name)
        {
            if (name.IsEmpty()) return null;
            var t = TextUtility.Restrict(name, CookieChars);
            while (t.StartsWith("$")) t = t.Substring(1);
            return t;
        }

        /// <summary>修剪 Cookie 的 Value 文本。</summary>
        public static string CookieValue(string value)
        {
            if (value.IsEmpty()) return value;
            var t = TextUtility.Restrict(value, CookieChars);
            if (t != value)
            {
                t = TextUtility.EncodeUrl(value);
                t = TextUtility.Restrict(t, CookieChars);
            }
            return t;
        }

        static string CookieExpires(DateTime dt)
        {
            // Expires=Wed, 21 Oct 2015 07:28:00 GMT;

            var utc = dt.ToUniversalTime();
            var sb = new StringBuilder();

            var week = utc.DayOfWeek.ToString().Substring(0, 3);
            sb.Append(week);

            sb.Append(", ");

            var day = utc.Day;
            if (day < 10) sb.Append("0");
            sb.Append(day);

            sb.Append(" ");

            var month = null as string;
            switch (utc.Month)
            {
                case 1: month = "Jan"; break;
                case 2: month = "Feb"; break;
                case 3: month = "Mar"; break;
                case 4: month = "Apr"; break;
                case 5: month = "May"; break;
                case 6: month = "Jun"; break;
                case 7: month = "Jul"; break;
                case 8: month = "Aug"; break;
                case 9: month = "Sep"; break;
                case 10: month = "Oct"; break;
                case 11: month = "Nov"; break;
                case 12: month = "Dec"; break;
            }
            sb.Append(month);

            sb.Append(" ");

            var year = utc.Year;
            if (year < 1000) sb.Append("0");
            if (year < 100) sb.Append("0");
            if (year < 10) sb.Append("0");
            sb.Append(year);

            sb.Append(" ");

            var time = ClockUtility.Lucid(utc, false, true, true, false);
            sb.Append(time);

            sb.Append(" GMT");
            return sb.ToString();
        }

        /// <summary>获取 Cookies 中第一个匹配到的值。</summary>
        public static string GetValue(this CookieCollection cookies, string name)
        {
            if (cookies == null || name.IsEmpty()) return null;

            var count = cookies.Count;
            for (var i = 0; i < count; i++)
            {
                var cookie = cookies[i];
                var n = cookie.Name;
                if (name == n) return cookie.Value;
            }

            var lower = TextUtility.Lower(name);
            for (var i = 0; i < count; i++)
            {
                var cookie = cookies[i];
                var n = TextUtility.Lower(cookie.Name);
                if (lower == n) return cookie.Value;
            }

            return null;
        }

        /// <summary>添加 Cookie 到集合。</summary>
        /// <param name="cookies">Cookie 集合。</param>
        /// <param name="name">名称。</param>
        /// <param name="value">值。</param>
        /// <param name="path">应用的路径。</param>
        /// <exception cref="CookieException"></exception>
        public static void Add(this CookieCollection cookies, string name, string value, string path = "/") => Add(cookies, name, value, path, null);

        /// <summary>添加 Cookie 到集合。</summary>
        /// <param name="cookies">Cookie 集合。</param>
        /// <param name="name">名称。</param>
        /// <param name="value">值。</param>
        /// <param name="path">应用的路径。</param>
        /// <param name="expires">过期时间。</param>
        /// <exception cref="CookieException"></exception>
        public static void Add(this CookieCollection cookies, string name, string value, DateTime expires, string path = "/") => Add(cookies, name, value, path, new Class<DateTime>(expires));

        /// <summary>添加 Cookie 到集合。</summary>
        /// <param name="cookies">Cookie 集合。</param>
        /// <param name="name">名称。</param>
        /// <param name="value">值。</param>
        /// <param name="path">应用的路径。</param>
        /// <param name="expires">过期时间。</param>
        /// <exception cref="CookieException"></exception>
        public static void Add(this CookieCollection cookies, string name, string value, string path, DateTime expires) => Add(cookies, name, value, path, new Class<DateTime>(expires));

        static void Add(this CookieCollection cookies, string name, string value, string path, Class<DateTime> expires)
        {
            if (cookies == null) return;
            name = CookieName(name);
            value = CookieName(value) ?? "";
            if (string.IsNullOrEmpty(name)) return;
            var cookie = new Cookie(name, value);
            cookie.Path = string.IsNullOrEmpty(path) ? "/" : path;
            if (expires != null) cookie.Expires = expires.Value;
            cookies.Add(cookie);
        }

        /// <summary>遍历 Cookie 集合。</summary>
        public static void ForEach(this CookieCollection cookies, Action<Cookie> action)
        {
            if (cookies == null || action == null) return;
            var count = cookies.Count;
            for (var i = 0; i < count; i++)
            {
                var cookie = cookies[i];
                if (cookie == null) continue;
                action.Invoke(cookie);
            }
        }

        internal static CookieCollection ParseCookies(HttpHeaders headers)
        {
            var cookies = new CookieCollection();
            if (headers == null) return cookies;

            var hvs = headers.GetValues("Cookie");
            foreach (var hv in hvs)
            {
                if (string.IsNullOrEmpty(hv)) continue;
                var lines = hv.Split(';');
                foreach (var line in lines)
                {
                    var e = line.IndexOf("=");
                    if (e < 0) continue;
                    var name = TextUtility.Left(line, e, true);
                    var value = TextUtility.Right(line, line.Length - e - 1, true);
                    if (string.IsNullOrEmpty(name)) continue;
                    if (string.IsNullOrEmpty(value)) continue;
                    cookies.Add(name, value);
                }
            }

            return cookies;
        }

        /// <summary>生成 Set-Cookie 的值。</summary>
        public static string SetCookie(Cookie cookie)
        {
            if (cookie == null) return null;

            var n = CookieName(cookie.Name);
            if (n.IsEmpty()) return null;

            var v = CookieValue(cookie.Value);
            if (v == null) v = "";

            var sb = new StringBuilder();
            sb.Append(n);
            sb.Append("=");
            sb.Append(v);

            var expires = cookie.Expires;
            if (expires > DateTime.MinValue)
            {
                sb.Append("; Expires=");
                sb.Append(CookieExpires(expires));
            }

            var domain = cookie.Domain;
            if (!string.IsNullOrEmpty(domain))
            {
                sb.Append("; Domain=");
                sb.Append(domain);
            }

            var path = cookie.Path;
            sb.Append("; Path=");
            sb.Append(string.IsNullOrEmpty(path) ? "/" : path);

            var secure = cookie.Secure;
            if (secure)
            {
                sb.Append("; Secure");
            }

            return sb.ToString();
        }

        /// <summary>生成 Set-Cookie 的值。</summary>
        public static string[] SetCookie(CookieCollection cookies)
        {
            if (cookies == null) return null;

            var count = cookies.Count;
            var lines = new List<string>(count);
            for (var i = 0; i < count; i++)
            {
                var cookie = cookies[i];
                var value = SetCookie(cookie);
                if (value.IsEmpty()) continue;
                lines.Add(value);
            }
            return lines.ToArray();
        }

        #endregion

        #region ApiController

        /// <summary>设置控制器属性。</summary>
        public static void SetProperties(ApiController controller, ApiRequest request, ApiResponse response, ApiOptions options)
        {
            if (controller == null) return;
            controller.Request = request;
            controller.Response = response;
            controller._options = options;
        }

        /// <summary>获取由控制器构造函数指定的初始化程序。</summary>
        public static Func<ApiController, bool> GetInitialier(this ApiController controller) => controller == null ? null : controller._func;

        /// <summary>获取由控制器构造函数指定的默认程序。</summary>
        public static Action<ApiController> GetDefault(this ApiController controller) => controller == null ? null : controller._default;

        /// <summary>获取选项。</summary>
        public static ApiOptions GetOptions(this ApiController controller) => controller == null ? null : controller._options;

        /// <summary>以 POST 转移请求到其它 URL。</summary>
        private static string Transfer(ApiController controller, string url, string application = null, string function = null)
        {
            if (controller == null || controller.Request == null || controller.Response == null) return "ApiControllser 无效。";
            if (url.IsEmpty()) return "ApiController 无效。";

            var s = Json.NewObject();
            s.SetProperty("random", TextUtility.Key());
            s.SetProperty("application", application.IsEmpty() ? controller.Request.Application : application);
            s.SetProperty("function", function.IsEmpty() ? controller.Request.Function : function);
            s.SetProperty("data", controller.Request.Data);
            s.SetProperty("session", controller.Request.Session);
            s.SetProperty("ticket", controller.Request.Ticket);
            s.SetProperty("page", controller.Request.Page);

            var c = new HttpClient();
            c.Url = url;
            c.Method = HttpMethod.POST;
            // TODO 设置 Request 的 Cookies。
            c.RequestData = s.ToString().Bytes();

            var e = c.Send();
            if (e != null) return e.Message;

            // TODO 解析 Response 的 Cookies。
            var r = Json.From(c.ResponseData.Text());

            if (r == null || !r.Available)
            {
                controller.Response.Error("请求失败。");
                return "请求失败。";
            }

            if (r["status"] != "ok")
            {
                controller.Response.Error(r["message"]);
                return r["message"];
            }

            controller.Response.Data.Reset(r.GetProperty("data"));
            return null;
        }

        /// <summary>创建指定类型的控制器，并引用当前控制器的 Request 和 Response。</summary>
        public static T Create<T>(this ApiController current) where T : ApiController, new()
        {
            var controller = new T();
            if (current != null)
            {
                controller.Request = current.Request;
                controller.Response = current.Response;
            }
            return controller;
        }

        /// <summary>使用默认控制器处理请求。</summary>
        public static void UseDefault(this ApiController current)
        {
            if (current == null) return;
            var options = GetOptions(current);
            if (options == null) return;
            var type = options.Default;
            if (type == null) return;
            var controller = null as ApiController;
            try
            {
                controller = (ApiController)Activator.CreateInstance(type);
                SetProperties(controller, current.Request, current.Response, options);
            }
            catch
            {
                return;
            }
            controller.GetInitialier()?.Invoke(controller);
        }

        #endregion

        #region ApiContext

        /// <summary>作为纯文本输出。</summary>
        /// <remarks>Content-Type: text/plain</remarks>
        public static void Text(ApiContext context, string text)
        {
            if (context == null) return;
            Model(context.Response, new ApiTextModel(text, "text/plain"));
        }

        /// <summary>设置 status 为 error，并设置 message 的内容。</summary>
        public static void Error(ApiContext context, string text)
        {
            if (context == null) return;
            Error(context.Response, text);
        }

        #endregion

        #region ApiRequest

        /// <summary>获取 URL 路径段，不存在的段为 NULL 值。可要求解码。</summary>
        public static string Segmental(this ApiRequest request, int index = 3, bool decode = false)
        {
            if (request == null) return null;
            if (request._segmentals == null) request._segmentals = Segmentals(request.Url);
            return Segmental(request._segmentals, index, decode);
        }

        /// <summary>获取参数，指定可能的参数名，默认将修剪参数值。</summary>
        /// <remarks>当从 URL 中获取参数时将解码。</remarks>
        public static string Parameter(this ApiRequest request, params string[] names) => Parameter(request, true, names);

        /// <summary>获取参数，指定可能的参数名，可要求修剪参数值。</summary>
        /// <remarks>当从 URL 中获取参数时将解码。</remarks>
        public static string Parameter(ApiRequest request, bool trim, params string[] names)
        {
            if (request == null) return null;
            if (names == null || names.Length < 1) return null;

            var dedupNames = new List<string>(names.Length);
            var lowerNames = new List<string>(names.Length);
            foreach (var name in names)
            {
                if (string.IsNullOrEmpty(name)) continue;
                if (dedupNames.Contains(name)) continue;
                else dedupNames.Add(name);

                var lower = TextUtility.Lower(name);
                if (lowerNames.Contains(lower)) continue;
                else lowerNames.Add(lower);
            }

            var matched = false;

            // POST 优先。
            var data = request.Data;
            if (data != null && data.IsObject)
            {
                var properties = data.GetProperties();
                if (properties != null)
                {
                    // Json 区分大小写，先全字匹配。
                    foreach (var property in properties)
                    {
                        if (!property.IsProperty) continue;
                        var name = property.Name;
                        if (!dedupNames.Contains(name)) continue;
                        var value = property.Value;
                        if (value == null) continue;
                        matched = true;
                        var text = value.ToString();
                        if (trim) text = TextUtility.Trim(text);
                        if (!string.IsNullOrEmpty(text)) return text;
                    }

                    // 以小写模糊匹配。
                    foreach (var property in properties)
                    {
                        if (!property.IsProperty) continue;
                        var name = TextUtility.Lower(property.Name);
                        if (!lowerNames.Contains(name)) continue;
                        var value = property.Value;
                        if (value == null) continue;
                        matched = true;
                        var text = value.ToString();
                        if (trim) text = TextUtility.Trim(text);
                        if (!string.IsNullOrEmpty(text)) return text;
                    }
                }
            }

            // 从已解析的 Get 参数中搜索。
            if (request.Parameters != null)
            {
                var value = Parameter(request.Parameters, trim, names);
                if (!string.IsNullOrEmpty(value)) return value;
                if (value != null) matched = true;
            }

            return matched ? "" : null;
        }

        #endregion

        #region ApiResponse

        internal static ApiModel Model(ApiResponse response, ApiModel model)
        {
            if (response == null && model == null) return null;
            response.Model = model;
            return model;
        }

        /// <summary>设置响应。</summary>
        public static string Respond(ApiResponse response, Json data, bool lower = true)
        {
            if (response == null) return "Response 对象无效。";
            if (data != null)
            {
                if (lower) data = Json.Lower(data);
                response.Data = data;
            }
            return null;
        }

        /// <summary>设置响应，当发生错误时设置响应。返回错误信息。</summary>
        public static string Respond(ApiResponse response, IList list, bool lower = true, int depth = -1, bool force = false)
        {
            if (response == null) return "Response 对象无效。";

            if (list == null)
            {
                var error = "列表对象无效。";
                response.Error(error);
                return error;
            }

            var json = Json.From(list, lower, depth, force);
            if (json == null || !json.Available)
            {
                var error = "列表无法序列化。";
                response.Error(error);
                return error;
            }

            if (response.Data == null) response.Data = Json.NewObject();
            response.Data.SetProperty("count", list.Count);
            response.Data.SetProperty("list", Json.From(list, lower, depth, force));
            return null;
        }

        /// <summary>设置响应，当发生错误时设置响应。返回错误信息。</summary>
        public static string Respond(ApiResponse response, IRecord record, bool lower = true)
        {
            if (response == null) return "Response 对象无效。";

            if (record == null)
            {
                var error = "记录无效。";
                response.Error(error);
                return error;
            }

            var json = Json.From(record, lower);
            if (json == null || !json.Available)
            {
                var error = "记录无法序列化。";
                response.Error(error);
                return error;
            }

            if (response.Data == null) response.Data = Json.NewObject();
            response.Data.Reset(json);
            return null;
        }

        /// <summary>设置 status 为 error，并设置 message 的内容。</summary>
        public static void Error(ApiResponse response, string message = "未知错误。")
        {
            if (response == null) return;
            response.Model = null;
            response.Status = "error";
            response.Message = message ?? TextUtility.Empty;
        }

        /// <summary>设置 status 为 exception，并设置 message 的内容。</summary>
        public static void Exception(ApiResponse response, Exception exception, bool setData = true)
        {
            if (response == null) return;
            response.Model = null;
            response.Status = "exception";

            if (exception != null)
            {
                try
                {
                    if (setData)
                    {
                        var json = ToJson(exception);
                        response.Message = json["message"];
                        response.Data = json;
                    }
                    else
                    {
                        response.Message = exception.Message();
                    }
                }
                catch { }
            }
        }

        private static Json ToJson(Exception exception, bool withInner = true)
        {
            if (exception == null) return ToJson(new NullReferenceException());

            var json = Json.NewObject();
            json.SetProperty("type", exception.GetType().FullName);
            json.SetProperty("message", exception.Message);

            var st = exception.StackTrace;
            if (!string.IsNullOrEmpty(st))
            {
                var stack = Json.NewArray();
                var split = st.Split('\r', '\n');
                foreach (var line in split)
                {
                    var trim = TextUtility.Trim(line);
                    if (string.IsNullOrEmpty(trim)) continue;
                    stack.AddItem(trim);
                }
                json.SetProperty("stack", stack);
            }

            if (withInner)
            {
                var iex = exception.InnerException;
                if (iex != null) json.SetProperty("inner", ToJson(exception, false));
            }

            return json;
        }

        /// <summary>停止 Invoker 对返回值的处理。</summary>
        public static void StopReturn(ApiResponse response)
        {
            if (response == null) return;
            response.StopReturn = true;
        }

        /// <summary>生成 Response　的 Json 实例。</summary>
        public static string ToJson(this ApiResponse response, ApiOptions options = null)
        {
            if (response == null) return "{}";
            if (string.IsNullOrEmpty(response.Status)) response.Status = "ok";

            var json = Json.NewObject();
            if (options == null) options = new ApiOptions();

            // 执行时间。
            if (options.WithClock)
            {
                json.SetProperty("clock", ClockUtility.Lucid(DateTime.Now));
            }

            // 持续时间。
            if (options.WithDuration)
            {
                if (response.Duration.NotEmpty()) json.SetProperty("duration", response.Duration);
            }

            // 随机值。
            var random = response.Random;
            if (!string.IsNullOrEmpty(random)) json.SetProperty("random", random);

            // 调用。
            if (options.WithTarget)
            {
                json.SetProperty("application", response.Application);
                json.SetProperty("function", response.Function);
            }

            // 状态。
            json.SetProperty("status", (TextUtility.IsBlank(response.Status) ? TextUtility.Empty : response.Status.ToLower()));
            if (!string.IsNullOrEmpty(response.Message)) json.SetProperty("message", response.Message);

            // 用户数据。
            if (response.Message == "exception" && !options.WithException) json.SetProperty("data");
            else json.SetProperty("data", response.Data);

            var indented = response.Indented || options.JsonIndent;
            var text = json.ToString(indented);
            return text;
        }

        #endregion

        #region ApiResult

        /// <summary>对 HTTP 结果设置文件名。</summary>
        /// <param name="result">结果。</param>
        /// <param name="name">文件名（未编码）。</param>
        public static void SetAttachemnt(this HeadResult result, string name)
        {
            if (result == null) throw new ArgumentNullException(nameof(result));
            if (name.IsEmpty()) throw new ArgumentNullException(nameof(name));

            var encoded = TextUtility.EncodeUrl(name);
            result.Headers.Add("Content-Disposition", $"attachment; filename={encoded}");
        }

        #endregion

        #region ApiFunction Parameters

        internal static object[] ReadParameters(ApiRequest request, ApiFunction function)
        {
            if (request == null || function == null) return null;
            return ReadParameters(request, function.Parameters);
        }

        /// <summary>为带有形参的 Function 准备实参。</summary>
        /// <param name="request">API 请求模型。</param>
        /// <param name="parameters">Function 的参数信息。</param>
        /// <returns>实参。</returns>
        public static object[] ReadParameters(ApiRequest request, ParameterInfo[] parameters)
        {
            if (request == null || parameters == null || parameters.Length < 1) return null;
            var apiParameters = parameters.Map(x => ApiParameter.Parse(x) ?? throw new Exception($"参数【{x.Name}】无效。"));
            return ReadParameters(request, apiParameters);
        }

        /// <summary>为带有形参的 Function 准备实参。</summary>
        /// <param name="request">API 请求模型。</param>
        /// <param name="parameters">Function 的参数信息。</param>
        /// <returns>实参。</returns>
        public static object[] ReadParameters(ApiRequest request, ApiParameter[] parameters)
        {
            if (request == null || parameters == null || parameters.Length < 1) return null;

            if (parameters == null) return null;

            var count = parameters.Length;
            if (count < 1) return null;

            // 当 Function 仅有一个参数时，尝试生成模型。
            if (count == 1 && parameters[0] != null)
            {
                var parameterName = parameters[0].Name;
                var parameterType = parameters[0].Type;

                // POST
                if (request.Method == HttpMethod.POST)
                {
                    // string
                    if (parameterType.Equals(typeof(string))) return new object[] { request.Parameters.GetValue(parameterName, true) };

                    // json
                    if (parameterType.Equals(typeof(Json))) return new object[] { request.PostJson };

#if !NET20
                    // dynamic
                    if (parameterType.Equals(typeof(object)))
                    {
                        try
                        {
                            // var expando = new System.Dynamic.ExpandoObject();
                            // var dict = expando as IDictionary<string, object>;
                            var expando = new ObjectSet(false, true);
                            var dict = expando.Origin();
                            if (request.Form != null)
                            {
                                foreach (var kvp in request.Form)
                                {
                                    if (dict.ContainsKey(kvp.Key)) continue;
                                    dict.Add(kvp.Key, kvp.Value);
                                }
                            }
                            else if (request.PostJson)
                            {
                                var jprops = request.PostJson.GetProperties();
                                foreach (var jprop in jprops)
                                {
                                    var name = jprop.Name;
                                    if (dict.ContainsKey(name)) continue;
                                    var value = jprop.Value;
                                    if (value != null)
                                    {

                                    }
                                    dict.Add(name, value);
                                }
                            }
                            return new object[] { expando };
                        }
                        catch { }
                    }
#endif

                    // class
                    if (parameterType.IsClass)
                    {
                        try
                        {
                            var entity = ReadParameter(request.Data, parameterType);
                            if (entity == null) entity = ReadParameter(request.PostJson, parameterType);
                            if (entity != null) return new object[] { entity.Value };
                        }
                        catch { }
                    }

                    if (request.PostJson) return new object[] { request.PostJson };
                    if (request.Form != null) return new object[] { request.Form };
                    if (request.PostText.NotEmpty()) return new object[] { request.PostText };
                    return new object[] { request.PostData };
                }
                else
                {
                    // string
                    if (parameterType.Equals(typeof(string))) return new object[] { request.Parameters.GetValue(parameterName, true) };

                    // json
                    if (parameterType.Equals(typeof(Json))) return new object[] { Json.From(request.Parameters.GetValue(parameterName, true)) };
                }
            }

            var values = new object[count];
            for (var i = 0; i < count; i++)
            {
                if (parameters[i] != null)
                {
                    var name = parameters[i].Name;
                    var type = parameters[i].Type;
                    var text = Parameter(request, name);
                    values[i] = ReadParameter(text, type);
                }
            }
            return values;
        }

        static Class<object> ReadParameter(Json json, Type type)
        {
            if (json)
            {
                if (json.IsObject)
                {
                    var properties = json.GetProperties();
                    if (properties.Length > 0)
                    {
                        var entity = Json.Object(type, json, true, null, true);
                        return new Class<object>(entity);
                    }
                }
                if (json.IsArray)
                {
                    var items = json.GetItems();
                    if (items.Length > 0)
                    {
                        var entity = Json.Object(type, json, true, null, true);
                        return new Class<object>(entity);
                    }
                }
            }
            return null;
        }

        static object ReadParameter(string text, Type type)
        {
            if (type.Equals(typeof(object)) || type.Equals(typeof(string))) return text;

            if (type.Equals(typeof(bool))) return NumberUtility.Boolean(text);
            if (type.Equals(typeof(float))) return NumberUtility.Single(text);
            if (type.Equals(typeof(double))) return NumberUtility.Double(text);
            if (type.Equals(typeof(decimal))) return NumberUtility.Decimal(text);
            if (type.Equals(typeof(byte))) return NumberUtility.Byte(text);
            if (type.Equals(typeof(sbyte))) return NumberUtility.SByte(text);
            if (type.Equals(typeof(short))) return NumberUtility.Int16(text);
            if (type.Equals(typeof(ushort))) return NumberUtility.UInt16(text);
            if (type.Equals(typeof(int))) return NumberUtility.Int32(text);
            if (type.Equals(typeof(uint))) return NumberUtility.UInt32(text);
            if (type.Equals(typeof(long))) return NumberUtility.Int64(text);
            if (type.Equals(typeof(ulong))) return NumberUtility.UInt64(text);

            if (type.Equals(typeof(byte[]))) return TextUtility.FromBase64(text);
            if (type.Equals(typeof(Json))) return Json.From(text);

            return type.IsValueType ? Activator.CreateInstance(type) : null;
        }

        #endregion

        #region Enumerate Entries

        internal static Json Enumerate(IEnumerable<ApiApplication> applications, ApiOptions options)
        {
            var list = Json.NewArray();
            var count = 0;
            if (applications != null)
            {
                foreach (var app in applications)
                {
                    if (app == null) continue;
                    if (app.Hidden) continue;
                    list.AddItem(app.ToJson(options));
                    count = count + 1;
                }
            }
            var json = Json.NewObject();
            json.SetProperty("count", count);
            json.SetProperty("applications", list);
            return json;
        }

        internal static Json Enumerate(IEnumerable<ApiFunction> functions, ApiOptions options)
        {
            var count = 0;
            var list = Json.NewArray();
            if (functions != null)
            {
                foreach (var func in functions)
                {
                    if (func == null) continue;
                    if (func.Hidden) continue;
                    list.AddItem(func.ToJson(options));
                    count = count + 1;
                }
            }
            var json = Json.NewObject();
            json.SetProperty("count", count);
            json.SetProperty("functions", list);
            return json;
        }

        #endregion

    }

}
